package com.moyu.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.moyu.common.core.contants.CacheConstants;
import com.moyu.common.core.contants.Constants;
import com.moyu.common.core.contants.HttpConstants;
import com.moyu.common.core.domain.LoginUser;
import com.moyu.common.core.domain.PermissionLoginDTO;
import com.moyu.common.core.domain.R;
import com.moyu.common.core.domain.RoleLoginDTO;
import com.moyu.common.core.enums.ResultCode;
import com.moyu.common.core.utils.ThreadLocalUtils;
import com.moyu.common.redis.service.RedisService;
import com.moyu.common.security.exception.ServiceException;
import com.moyu.common.security.service.TokenService;
import com.moyu.user.domain.role.Role;
import com.moyu.user.domain.role.vo.RoleListVO;
import com.moyu.user.domain.user.User;
import com.moyu.user.domain.user.dto.*;
import com.moyu.user.domain.user.vo.*;
import com.moyu.user.mapper.PermissionMapper;
import com.moyu.user.mapper.RoleMapper;
import com.moyu.user.mapper.UserMapper;
import com.moyu.user.service.IUserService;
import com.moyu.user.utils.BCryptUtils;
import com.moyu.user.utils.BuildAccountUtil;
import com.moyu.user.utils.EmailCodeUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 *
 * @Author 赵彦博
 * @Date 2025/3/25 18:30
 */
@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private BuildAccountUtil buildAccount;

    @Value("${spring.mail.username}")
    private String mailAccount;

    @Value("${spring.mail.password}")
    private String mailPassword;

    @Value("${spring.mail.emailSMTPHost}")
    private String emailSMTPHost;

    @Autowired
    private EmailCodeUtil emailCodeUtil;

    @Autowired
    private TokenService tokenService;

    @Value("${jwt.secret}")
    private String secret;

    @Override
    public R register(HttpServletRequest request, UserSaveDTO userSaveDTO) {
        // 使用邮箱进行注册前的信息验证
        String emailAddress = userSaveDTO.getEmailAddress();
        String password = userSaveDTO.getPassword();
        String passwordRepeat = userSaveDTO.getPasswordRepeat();
        String verificationCode = userSaveDTO.getVerificationCode();
        R r = new R();
        // 状态信息
        int ret = checkMsg(request, emailAddress, password, passwordRepeat, verificationCode);
        if (ret == 1) {
            boolean res = checkEmail(emailAddress);
            if (res) {
                // 新增user对象
                User user = new User();
                String account = buildUser(emailAddress);
                user.setUserAccount(account);
                user.setNickName("新人用户" + emailAddress);
                user.setEmailAddress(emailAddress);
                user.setPassword(BCryptUtils.encryptPassword(passwordRepeat));
                user.setGender("未知");
                user.setProfilePicture("@/assets/images/avatar01.png");
                user.setPhoneNumber("");
                user.setStatus(1);
                r.setCode(0);
                r.setMsg("注册成功，您的账号为：" + account + ",后续您可通过账号进行登录~");
                r.setData(user);
                ThreadLocalUtils.set(Constants.USER_ID, 1l);
                userMapper.insert(user);
                Long userId = user.getUserId();
                if (userId == null) {
                    // 如果无法获取ID，尝试通过账号查询
                    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
                    User dbUser = userMapper.selectOne(queryWrapper.eq(User::getUserAccount, account));
                    userId = dbUser != null ? dbUser.getUserId() : null;
                }
                if (userId != null) {
                    try {
                        // 查询普通用户角色ID
                        Long roleId = getDefaultRoleId();
                        if (roleId != null) {
                            // 分配角色
                            UserRoleDTO userRole = new UserRoleDTO();
                            userRole.setUserId(String.valueOf(userId));
                            userRole.setRoleId(String.valueOf(roleId));
                            userMapper.addUserRole(userRole);
                        }
                    } catch (Exception e) {
                        log.error("分配角色失败", e);
                        // 角色分配失败不影响注册流程
                    }
                }

                r.setCode(0);
                r.setMsg("注册成功，您的账号为：" + account + ",后续您可通过账号进行登录~");
                r.setData(user);
            } else {
                throw new ServiceException(ResultCode.AILED_USER_EXISTS);
            }
        } else {
            r.setCode(-1);
            r.setData(null);
            if (ret == 2) {
                r.setMsg("您当前还未发送验证码，请先完成发送验证码~");
            } else if (ret == 3) {
                r.setMsg("邮箱信息有误，请检查后重新发送~");
            } else if (ret == 4) {
                r.setMsg("验证码过期，请重新发送~");
            } else if (ret == 5) {
                r.setMsg("确认密码和初始密码不一致，请重新输入~");
            } else if (ret == 6) {
                r.setMsg("验证码错误，请检查后重新输入~");
            }
        }
        return r;
    }

    private Long getDefaultRoleId() {
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        Role role = roleMapper.selectOne(queryWrapper.eq(Role::getRoleName, "普通用户"));
        return role.getRoleId();
    }

    /**
     * 生成10位随机账号
     *
     * @return 账号
     */
    private String generateAccount() {
        buildAccount.init();
        return buildAccount.getPersonId();
    }

    public String buildUser(String emailAddress) {
        String account = generateAccount();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, account));
        if (user != null) {
            account = generateAccount();
        }
        return account;
    }

    private boolean checkEmail(String emailAddress) {
        List<User> sysUserList = userMapper.selectList(new LambdaQueryWrapper<User>()
                .eq(User::getEmailAddress, emailAddress));
        if (CollectionUtil.isNotEmpty(sysUserList)) {
            //已经被使用
            return false;
        }
        //已未被使用
        return true;
    }

    private int checkMsg(HttpServletRequest request, String emailAddress, String password, String passwordRepeat, String verificationCode) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            //未发送验证码
            return 2;
        }
        Object emailObj = session.getAttribute("emailAddress");
        if (emailObj == null) {
            //未发送验证码
            return 2;
        }
        String email = String.valueOf(emailObj);
        if (!email.equals(emailAddress)) {
            //表示使用同一个邮箱
            return 3;
        }
        Boolean res = redisService.hasKey(email);
        if (!res) {
            return 4;
        }
        if (!password.equals(passwordRepeat)) {
            //密码不一致
            return 5;
        }
        String checkCode = redisService.getCacheObject(email, String.class);
        if (!checkCode.equals(verificationCode)) {
            return 6;
        }
        redisService.deleteObject(email);
        return 1;
    }

    @Override
    public R sendEmailCode(HttpServletRequest request, String emailAddress) {
        long stime = System.currentTimeMillis();
        R r = new R();
        HttpSession session = request.getSession(true);
        if (session != null && session.getAttribute("emailAddress") != null) {
            //说明发送过验证码
            String key = (String) session.getAttribute("emailAddress");
            //判断验证码是否过期
            Boolean res = redisService.hasKey(key);
            if (res) {
                //表示key未过期，不可以重新发送验证码
                return new R(-1, "已经发送过验证码，短时间内不可以重复发送", false);
            }
            //走到这，就表示验证码过期了，继续往下走发送验证码
            //移除旧的session当中存储的信息
            session.removeAttribute("emailAddress");
        }
        List<Object> ret = sendCode(request, emailAddress);
        Object obj = ret.get(1);
        Boolean boolObj = Boolean.valueOf(obj.toString());
        String emailCode = String.valueOf(ret.get(0));
        if (boolObj) {
            session.setAttribute("emailAddress", emailAddress);
            System.out.println("============================================================================");
            redisService.setCacheObject(emailAddress, emailCode, CacheConstants.VERIFICATIONCODE_EXP, TimeUnit.SECONDS);
            r.setCode(1000);
            r.setMsg("验证码发送成功，验证码将于120秒之后过期，请尽快完成注册~");
            r.setData(true);
        } else {
            r.setCode(-1);
            r.setMsg("邮箱信息有误，请检查邮箱的正确性~");
            r.setData(false);
        }
        long etime = System.currentTimeMillis();
        System.out.printf("执行时长：%d 毫秒.%n", (etime - stime));
        return r;
    }

    public List<Object> sendCode(HttpServletRequest request, String emailAddress) {
        List<Object> list = new ArrayList<>();
        SimpleEmail email = new SimpleEmail();
        email.setSslSmtpPort("465");
        email.setHostName(emailSMTPHost);
        email.setAuthentication(mailAccount, mailPassword);
        email.setCharset("UTF-8");
        email.setSSLOnConnect(true);
        String emailCode = "";
        try {
            email.addTo(emailAddress);
            email.setFrom(mailAccount);
            emailCode = generateVerificationCode();
            email.setSubject("【技术问答与在线互助论坛系统】");
            email.setMsg("尊敬的用户，您好，您的邮箱验证码为：" + emailCode + ",您当前正在执行邮箱绑定服务，如非本人操作，请忽略此邮件~");
            email.send();
            list.add(emailCode);
            list.add(true);
            return list;
        } catch (EmailException e) {
            e.printStackTrace();
            list.add(emailCode);
            list.add(false);
            return list;
        }
    }

    /**
     * 生成6位随机验证码
     *
     * @return 账号
     */
    private String generateVerificationCode() {
        emailCodeUtil.init();
        return emailCodeUtil.getEmailCode();
    }

    @Override
    public R<String> login(String userAccount, String password) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .eq(User::getUserAccount, userAccount));
        if (user == null) {
            return R.fail(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        Set<RoleLoginDTO> roleLoginDTOS = roleMapper.selectRoleByUserId(user.getUserId());
        Set<PermissionLoginDTO> permissionLoginDTOS = roleMapper.selectPermissionByUserId(user.getUserId());
        if (BCryptUtils.matchesPassword(password, user.getPassword())) {
            return R.ok(tokenService.createToken(user.getUserId(),
                    secret, user.getUserAccount(), user.getNickName(), user.getStatus(), user.getProfilePicture(), roleLoginDTOS, permissionLoginDTOS));
        }
        return R.fail(ResultCode.FAILED_LOGIN);
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setUserId(loginUser.getUserId());
        loginUserVO.setNickName(loginUser.getNickName());
        loginUserVO.setProfilePicture(loginUser.getProfilePicture());
        LoginUserVO loginUserVO1 = userMapper.selectData(loginUser.getUserId());
        loginUserVO.setLikeCount(loginUserVO1.getLikeCount());
        loginUserVO.setPostCount(loginUserVO1.getPostCount());
        loginUserVO.setVisitCount(loginUserVO1.getVisitCount());
        return R.ok(loginUserVO);
    }

    @Override
    public List<UserQueryVO> list(UserQueryDTO userQueryDTO) {
        PageHelper.startPage(userQueryDTO.getPageNum(), userQueryDTO.getPageSize());
        return userMapper.list(userQueryDTO);
    }

    @Override
    public int updateStatus(UserDTO userDTO) {
        User user = userMapper.selectById(userDTO.getUserId());
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setStatus(userDTO.getStatus());
//        userCacheManager.updateStatus(user.getUserId(), userDTO.getStatus());
        return userMapper.updateById(user);
    }

    @Override
    public List<RoleListVO> getRole(UserQueryDTO userQueryDTO) {
        PageHelper.startPage(userQueryDTO.getPageNum(), userQueryDTO.getPageSize());
        return userMapper.getRole(userQueryDTO);
    }

    @Override
    public List<RoleListVO> getAvailableRoles(UserRoleQueryDTO queryDTO) {
        PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize());
        return userMapper.getAvailableRoles(queryDTO);
    }

    @Override
    public int addUserRole(UserRoleDTO dto) {
        return userMapper.addUserRole(dto);
    }

    @Override
    public int removeUserRole(UserRoleDTO dto) {
        return userMapper.removeUserRole(dto);
    }

    @Override
    public List<UserHotVO> getHotUser() {
        return userMapper.getHotUser();
    }

    @Override
    public DataTotalVO getTotal() {
        DataTotalVO dataTotalVO = new DataTotalVO();
        dataTotalVO.setUserCount(userMapper.getUserCount());
        dataTotalVO.setPostCount(userMapper.getPostCount());
        dataTotalVO.setReplyCount(userMapper.getReplyCount());
        return dataTotalVO;
    }

    @Override
    public int updateUserInfo(String token, UserUpdateDTO dto) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .eq(User::getUserId, dto.getUserId()));
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        int i = userMapper.updateUserInfo(dto);
        if (i > 0) {
            LoginUser loginUser = new LoginUser();
            Set<RoleLoginDTO> roleLoginDTOS = roleMapper.selectRoleByUserId(user.getUserId());
            Set<PermissionLoginDTO> permissionLoginDTOS = roleMapper.selectPermissionByUserId(user.getUserId());
            loginUser.setUserId(String.valueOf(dto.getUserId()));
            loginUser.setAccount(user.getUserAccount());
            loginUser.setRoles(roleLoginDTOS);
            loginUser.setPermissions(permissionLoginDTOS);
            loginUser.setNickName(dto.getNickName());
            loginUser.setProfilePicture(dto.getProfilePicture());
            loginUser.setState(user.getStatus());
            if (token.startsWith("Bearer ")) {
                token = token.substring(7); // 去掉前7个字符（"Bearer "）
            }
            String userKey = tokenService.getUserKey(token, secret);
            String tokenKey = tokenService.getTokenKey(userKey);
            redisService.setCacheObject(tokenKey, loginUser, CacheConstants.EXP, TimeUnit.MINUTES);
        }
        return i;
    }

    @Override
    public int changePassword(UserUpdatePasswordDTO dto) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .eq(User::getUserId, dto.getUserId()));
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        String password = user.getPassword();
        String oldPassword = dto.getOldPassword();
        String newPassword = dto.getNewPassword();
        String confirmPassword = dto.getConfirmPassword();
        if (!BCryptUtils.matchesPassword(oldPassword, password)) {
            throw new ServiceException(ResultCode.FAILED_PASSWORD);
        }
        if (oldPassword.equals(confirmPassword)) {
            throw new ServiceException(ResultCode.FAILED_PASSWORD_TWO);
        }
        String encryptPassword = BCryptUtils.encryptPassword(newPassword);
        dto.setNewPassword(encryptPassword);
        return userMapper.changePassword(dto);
    }

    @Override
    public List<UserPostListVO> getUserPosts(String userId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .eq(User::getUserId, userId));
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return userMapper.getUserPosts(userId);
    }

    @Override
    public int updateAvatar(String fileName, String userId) {
        return userMapper.updateAvatar(fileName,userId);
    }

    @Override
    public R<LoginUserVO> getUserInfoOther(String token,String userId) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        Boolean flag = true;
        if (!loginUser.getUserId().equals(userId)) {
            flag = false;
        }
        User user = userMapper.selectById(userId);
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setUserId(String.valueOf(user.getUserId()));
        loginUserVO.setNickName(user.getNickName());
        loginUserVO.setProfilePicture(user.getProfilePicture());
        LoginUserVO loginUserVO1 = userMapper.selectData(String.valueOf(user.getUserId()));
        loginUserVO.setLikeCount(loginUserVO1.getLikeCount());
        loginUserVO.setPostCount(loginUserVO1.getPostCount());
        loginUserVO.setVisitCount(loginUserVO1.getVisitCount());
        loginUserVO.setIsCurrent(flag);
        return R.ok(loginUserVO);
    }

    @Override
    public List<UserPostListVO> getUserFavorites(String userId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .eq(User::getUserId, userId));
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return userMapper.getUserFavorites(userId);
    }
}
